#! /usr/bin/env python
# -*- coding: utf-8 -*-

# *************************************************************
#     Filename @  sendmail.py
#       Author @  Huoty
#  Create date @  2015-07-05 09:25:55
#  Description @  邮件发送
# *************************************************************

import os
import re
import time
import sys
import getopt
import getpass
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
import logging
from logging import debug
logging.basicConfig(level = logging.DEBUG)

class SendMail(object):
    '''
    发送邮件
    '''
    def __init__(self, mail_host):
        '''
        构造函数，初始化邮件发送所必须的参数：
            mail_host： 邮件服务器主机地址,支持直接加端口号,例如 "smtp.XXX.com:25"
            mail_user： 邮箱用户名，仅用户名，例如 Huoty
            mail_passwd: 邮箱登录密码
            mail_sender: 邮件发送者，支持两种：一种直接是邮箱地址，例如 sudohuoty@163.com; 另一种是带发送者昵称的，例如 huoty<sudohuoty@163.com>
            mailto_list: 邮件接受者列表，支持群发
            mail_msg: 要发送的邮件信息，包括文本和附件
        '''
        self.mail_host = mail_host
        self.mail_user = ""
        self.mail_passwd = ""
        self.mail_sender = ""
        self.mailto_list = ""
        self.mail_msg = MIMEMultipart()

    def setsender(self, sender, passwd, tolist):
        '''
        设置发件人姓名和邮箱地址，邮箱密码，和收件人列表。sender可以是单纯的邮箱地址，例如 sudohuoty@163.com；也可以是带发送者姓名的邮件地址，例如 Huoty<sudohuoty@163.com>。tolist为收件人列表, 如果为群发, 则用列表或元组包含多个收件地址。
        '''
        print "Setting the sender information ..."
        debug(str("Sender is " + sender))

        # 提取邮箱用户名
        reg_result1 = re.search("^\w+<([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})>$", sender)
        if reg_result1:
            self.mail_user = reg_result1.group(1)
            debug(str("Mail user is " + self.mail_user))
        else:
            reg_result2 = re.search("^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$", sender)
            if reg_result2:
                self.mail_user = reg_result2.group(1)
                debug(str("Mail user is " + self.mail_user))
            else:
                print "Error：The email address of sender is a incorrect format!"
                return False

        self.mail_passwd = passwd
        self.mail_sender = sender
        self.mailto_list = tolist

        return True

    def setcontent(self, subject, content, subtype = "plain", charset = "utf-8", attachments = None):
        '''
        设置邮件内容：
            subject：邮件主题
            content：邮件文本内容
            subtype：发送的邮件类型，其值可以为 plain/text、html、misc, 为 html 时必须包含 body 标签
            charset：邮件内容的编码方式
            attachment：附件列表
        当邮件类型为 html 和 misc 时才需要传递 attachment 参数。当邮件类型为 misc，且文本内容为 html 形式，并包含媒体内容，则在 attachment 中传递媒体资源时，应将其加 “cid + 编号：” 前缀，例如 ”cid0：/home/konghy/temp/psb.jpg“
        '''
        print "Setting the mail content ..."

        self.mail_msg['Subject'] = subject
        self.mail_msg['From'] = self.mail_sender
        self.mail_msg['To'] = ";".join(self.mailto_list)
        self.mail_msg['Date'] = time.strftime("%Y-%m-%d", time.localtime())

        # initialize attachments
        if attachments is None:
            attachments = []

        # mail type is plain/text
        if subtype == "plain" or subtype == "text":
            self.mail_msg.attach(MIMEText(content, _subtype = subtype, _charset = charset))
        # mail type is html
        elif subtype == 'html':
            if len(attachments) == 0:
                self.mail_msg.attach(MIMEText(content, _subtype = subtype, _charset = charset))
            else:
                self.mail_msg.attach(MIMEText(content, _subtype = subtype, _charset = charset))

                for i, attachment in enumerate(attachments):
                    fp = open(attachment, 'rb')
                    mime = MIMEImage(fp.read())
                    fp.close()

                    mime.add_header('Content-Disposition', 'attachment', filename = os.path.basename(attachment))
                    mime.add_header('Content-ID', '<' + str(i) + '>')
                    mime.add_header('X-Attachment-Id', str(i))
                    self.mail_msg.attach(mime)
        # mail type is compound
        elif subtype == "misc":
            is_html = re.search("<body>.*</body>", content, re.S)
            if len(attachments) == 0 and not is_html:
                self.mail_msg.attach(MIMEText(content, _subtype = 'plain', _charset = charset))
            else:
                if is_html:
                    self.mail_msg.attach(MIMEText(content, _subtype = 'html', _charset = charset))
                else:
                    self.mail_msg.attach(MIMEText(content, _subtype = 'plain', _charset = charset))

                for i, attachment in enumerate(attachments):
                    html_media = re.search("^cid(\d+):(.+)$", attachment)
                    if html_media: # 判断是否为html中包含的媒体
                        fp = open(html_media.group(2), 'rb')
                        mime = MIMEImage(fp.read())
                        fp.close()

                        mime.add_header('Content-Disposition', 'attachment', filename = os.path.basename(attachment))
                        mime.add_header('Content-ID', '<' + html_media.group(1) + '>')
                        mime.add_header('X-Attachment-Id', html_media.group(1))
                        self.mail_msg.attach(mime)
                    else: # 普通附件文件
                        fp = open(attachment, 'rb')
                        att = MIMEText(fp.read(), 'base64', 'utf-8')
                        fp.close()

                        att.add_header('Content-Type', 'application/octet-stream')
                        att.add_header('Content-Disposition', 'attachment', filename = os.path.basename(attachment))
                        self.mail_msg.attach(att)
        else:
            print "Temporarily does not support this type of email:", subtype
            return False

        #debug(self.mail_msg.as_string())
        return True

    def startup(self):
        '''
        连接邮件服务器,并发送邮件
        '''
        print "Sending mail ..."

        try:
            server = smtplib.SMTP()
            server.connect(self.mail_host)
            server.login(self.mail_user, self.mail_passwd)
            server.sendmail(self.mail_sender, self.mailto_list, self.mail_msg.as_string())
            server.close()

            print "Successfully"
            return True

        except Exception, e:
            print "Failed to send mail:"
            print str(e)
            return False

def testsend():
    content = '''
<body>
Hello!

<p><img src="cid:0"></p>
I am Huoty.

How are you?

Give you a picture:
<p><img src="cid:1"></p>
</body>
'''
    mail = SendMail("smtp.163.com")
    mail.setsender("Huoty<sudohuoty@163.com>", "xxxxxx", ["sudohuoty@163.com", "1346632121@qq.com"])
    mail.setcontent("Test to send email by python", content, subtype = "misc", attachments = ["cid0:/home/konghy/temp/khy_face_130.png", "cid1:/home/konghy/temp/psb.jpg", "/home/konghy/temp/Python.png", "/home/konghy/temp/test.pdf"])
    mail.startup()

# Script starts from here

if __name__ == '__main__':
    #testsend()

    try:
        opts, args = getopt.getopt(sys.argv[1:], "hv", ["help", "version"])
    except getopt.GetoptError:
        print "Invalid option!"

    if opts:
        for o, a in opts:
            if o in ("-h", "--help"):
                print u'''
Usage: sendmail command

sendmail is a simple command line for sending email.

Commands:
    h | --help        Show usage
    v | --version     Show version
'''
                sys.exit()

            if o in ("-v", "--version"):
                print u"Sendmail version: sendmail/1.0 (all platforms)"
                print u"Sendmail built:   2015-07-07"
                print u"Sendmail author:  huoty"
                sys.exit()
    else:
        print "Please enter the following information:"
        mailserver = raw_input("Email server: ")
        mailsender = raw_input("Email sender: ")
        mailpassed = getpass.getpass("Emial passwd: ")

        receivers = raw_input("To list: ").split(",")
        mailtolist = []
        for receiver in receivers:
            mailtolist.append(receiver.strip())
        debug(mailtolist)

        mailsubject = raw_input("Email subject: ")

        mailtype = raw_input("Email type: ")
        while True:
            if not mailtype in ["plain", "text", "html", "misc"]:
                print "The type you entered is incorrect, please input again."
                mailtype = raw_input("Email type: ")
            else:
                break

        print "Email content: "
        mailcontent = ""
        while True:
            strline = raw_input()
            if strline == ">>>end":
                break
            else:
                mailcontent += strline + "\n"

        atts = raw_input("Email attachments: ").split(",")
        mailattachments = []
        for att in atts:
            mailattachments.append(att.strip())
        debug(mailattachments)

        print "\n"
        mail = SendMail(mailserver)
        mail.setsender(mailsender, mailpassed, mailtolist)
        mail.setcontent(mailsubject, mailcontent.strip(), subtype = mailtype, attachments = mailattachments)
        mail.startup()
